package com.xiaoxiaoyu.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

/**
 * @program: springDemo
 * @description: 通过package返回所有的class对象
 * @author: 小小鱼
 * @date: 2021-01-17 00:19
 **/
@Slf4j
public class ClassUtil<T> {

    public static final String FILE = "file";

    /**
     * 获取当前包名下的所有class
     * @param packageName
     * @return
     */
    public static Set<Class<?>> gainPackageClass(String packageName){
        //获得类加载器
        ClassLoader classLoader = gainClassLoader();
        //通过类加载器获得类资源
        URL resource = classLoader.getResource(packageName.replace(".", "/"));
        if(resource==null){
            log.warn("当前包[{}]下不存在任何类",packageName);
            return null;
        }
        Set<Class<?>> classSet=null;
        if(resource.getProtocol().equalsIgnoreCase(FILE)){
            classSet=new HashSet<>();
            //获取所有的file
            File file = new File(resource.getPath());
            //获取所有的classFile
            extractClassFile(classSet,file,packageName);
        }
        return classSet;
    }

    /**
     * 获取当前目录下所有的class文件
     * @param classSet
     * @param resourcesFile
     * @param packageName
     */
    private static void extractClassFile(final Set<Class<?>> classSet, File resourcesFile, final String packageName) {
        if(!resourcesFile.isDirectory()){
            return ;
        }
        //如果是文件夹 获取当前文件夹的所有目录
        File[] files = resourcesFile.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if(pathname.isDirectory()){
                    return true;
                }else{
                    String absolutePath = pathname.getAbsolutePath();
                    if(absolutePath.endsWith(".class")){
                        addToClass(absolutePath);
                    }
                }
                return false;
            }
            private void addToClass(String absolutePath) {
                //通过绝对路径获取相对路径的class URL
                //1.将/\替换成.
                String className = absolutePath.replace(File.separator, ".");
                //2.截取
                className=className.substring(className.indexOf(packageName));
                className=className.substring(0,className.lastIndexOf("."));
                //通过反射机制创建出Class
                Class<?> tarageClass = classLoader(className);
                classSet.add(tarageClass);
            }


        });

        if(files!=null){
            for (File file : files) {
                extractClassFile(classSet,file,packageName);
            }
        }

    }



    public static Class<?> classLoader(String className) {
        try {
          return  Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("classLoader加载出错。className={}",className);
            throw new RuntimeException(e);
        }
    }


    /**
     *
     * @param aClass 类对象
     * @param access 访问private 函数
     * @param <T> 泛型
     * @return 创建实例
     */
    public static <T> T newInstance(Class<?> aClass,Boolean access) {
        try {
            Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
            declaredConstructor.setAccessible(access);
            return  (T)declaredConstructor.newInstance();
        } catch (Exception e) {
            log.warn("没有无参构造！「{}」",aClass.getName());
            throw new RuntimeException(e);
        }
    }

    /**
     *  注入成员变量的值
     * @param field 成员变量
     * @param target 目标类
     * @param value 成员变量值
     * @param accessible 是否允许访问private
     */
    public static void setField(Field field,Class target,Object value,boolean accessible){
        field.setAccessible(accessible);
        try {
            field.set(target,value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }



    /**
     * 获取当前类的加载器
     * @return
     */
    public static ClassLoader gainClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    public static void main(String[] args) {
        Set<Class<?>> classSet = gainPackageClass("com.hzh.entity");
        for (Class<?> aClass : classSet) {
            System.out.println(aClass.getName());
        }

    }

}
